﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Media3D;
using System.Windows.Media.Imaging;
using System.ComponentModel;

using HelixToolkit.Wpf;
using EQEmu.Display3D;
using EQEmu.Spawns;
using EQEmu.Database;
using EQEmu;


namespace EQEmu.Spawns.Display3D
{
    public class ZoneSpawnsDisplay3D : IDisposable, IViewConstrainer
    {
        private GeometryModel3D _spawnModel;
        private GeometryModel3D _gridspawnModel;

        private ViewConstraints _constraints = null;
        private ZoneSpawns _zoneSpawns;
        private Model3D _model = new Model3DGroup();
        private Dictionary<spawn2, Model3DCollection> _mapping = new Dictionary<spawn2, Model3DCollection>();

        public ZoneSpawnsDisplay3D(ZoneSpawns zoneSpawns)
        {
            _zoneSpawns = zoneSpawns;
            _zoneSpawns.SpawnsCollectionChanged += _zoneSpawns_SpawnsCollectionChanged;
            MeshBuild();

            foreach (var sp in _zoneSpawns.Spawns)
            {
                CreateSpawn(sp);
            }
        }

        private GeometryModel3D GetSpawnModel()
        {
            return _spawnModel.CloneCurrentValue();
        }

        private GeometryModel3D GetGridSpawnModel()
        {
            return _gridspawnModel.CloneCurrentValue();
        }

        private void MeshBuild()
        {
            //build the reusable box for the nodes
            MeshBuilder builder = new MeshBuilder();
            builder.AddBox(new Point3D(0, 0, 0), 2, 2, 2);
            builder.AddArrow(new Point3D(0, 0, 0), new Point3D(0,3,0), 0.5, 1);
            _spawnModel = new GeometryModel3D(builder.ToMesh(), Materials.White);            

            builder = new MeshBuilder();
            builder.AddBox(new Point3D(0, 0, 0), 2, 2, 2);
            builder.AddArrow(new Point3D(0, 0, 0), new Point3D(0,3,0), 0.5, 1);
            _gridspawnModel = new GeometryModel3D(builder.ToMesh(), Materials.Yellow);
        }

        private void CreateSpawn(spawn2 spawn)
        {
            Model3DGroup group = Model as Model3DGroup;
            var sp = spawn as INotifyPropertyChanged;            

            Model3DCollection collection = new Model3DCollection();

            if (_mapping.ContainsKey(spawn) && _mapping[spawn] != null)
            {
                foreach (Model3D model in _mapping[spawn])
                {
                    group.Children.Remove(model);
                }
            }
            else
            {
                if (sp != null)
                {
                    sp.PropertyChanged += sp_PropertyChanged;
                }
            }

            if (_constraints != null &&
                !_constraints.IsPointWithinConstraints(new Point3D(spawn.x,spawn.y,spawn.z))) return;

            _mapping[spawn] = collection;

            GeometryModel3D box = spawn.pathgrid > 0 ? GetGridSpawnModel() : GetSpawnModel();
            Transform3DGroup transforms = new Transform3DGroup();            
            Transform3D headingRotate = new RotateTransform3D()
            {
                CenterX = spawn.x,
                CenterY = spawn.y,
                CenterZ = spawn.z,
                Rotation = new AxisAngleRotation3D(
                    new Vector3D(0, 0, -1), Functions.HeadingToDegrees(spawn.heading))
            };
            transforms.Children.Add(new TranslateTransform3D(spawn.x, spawn.y, spawn.z));
            transforms.Children.Add(headingRotate);
            box.Transform = transforms;
            collection.Add(box);
            
            foreach (Model3D model in collection)
            {
                group.Children.Add(model);
            }      
        }

        void sp_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            CreateSpawn(sender as spawn2);
        }

        private void RemoveSpawn(spawn2 spawn)
        {
            Model3DGroup group = Model as Model3DGroup;
            var sp = spawn as INotifyPropertyChanged;

            foreach (Model3D model in _mapping[spawn])
            {
                group.Children.Remove(model);
            }

            if (sp != null)
            {
                sp.PropertyChanged -= sp_PropertyChanged;
            }

            _mapping[spawn] = null;
        }

        void _zoneSpawns_SpawnsCollectionChanged(object sender, SpawnsCollectionChangedEventArgs args)
        {
            if (args.AddedSpawns != null)
            {
                foreach (var sp in args.AddedSpawns)
                {
                    CreateSpawn(sp);
                }
            }

            if (args.RemovedSpawns != null)
            {
                foreach (var sp in args.RemovedSpawns)
                {
                    RemoveSpawn(sp);
                }
            }
        }

        protected virtual void Dispose(bool disposeAll)
        {
            if (_zoneSpawns != null)
            {
                _zoneSpawns.SpawnsCollectionChanged -= _zoneSpawns_SpawnsCollectionChanged;
            }

            foreach (var sp in _zoneSpawns.Spawns.Cast<INotifyPropertyChanged>())
            {
                sp.PropertyChanged -= sp_PropertyChanged;
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }

        public void UpdateView(ViewConstraints constraints)
        {
            _constraints = constraints;
        }

        public Model3D Model
        {
            get
            {
                return _model;
            }
        }
    }
}
